# ==============================
# _*_ coding:utf-8 _*_
# _*_ by:zizle     _*_
# _*_ at:20230421  _*_
# ==============================
import json
import pathlib

import requests
from utils import datetime_utils
from utils.logger import Logger
from settings import FILE_CONFIG_FOLDER, FILE_STORAGE
from settings.tripartite import THS_REFRESH_TOKEN


# 同花顺接口API
class THSApi(object):
    __REFRESH_TOKEN = THS_REFRESH_TOKEN
    __ACCESS_TOKEN_KEY = 'Account'
    __MAIN_CODE_KEY = 'MainCode'
    TOKEN_EXPIRED = -1302

    def __init__(self):
        self.access_token = None
        self.main_codes = []  # 国内主力合约
        self.log_file_prefix = 'thsapi_'  # 日志文件名的前缀
        day_folder = 'tripartiteTemp/THS/realTimeQuotation/{}'.format(datetime_utils.now_yyyy_mm_dd())
        self.real_time_quotation_cache_folder = pathlib.Path(FILE_STORAGE).joinpath(day_folder)
        if not self.real_time_quotation_cache_folder.exists():
            self.real_time_quotation_cache_folder.mkdir(parents=True)

    @staticmethod
    def get_current_config(key: str = None):
        folder = pathlib.Path(FILE_CONFIG_FOLDER)
        config_file = folder.joinpath('ths_api.json')
        if config_file.exists():
            with open(config_file, 'r', encoding='utf8') as rf:
                cur_config = json.load(rf)
        else:
            cur_config = {}

        return cur_config.get(key) if key else cur_config, config_file

    def set_current_config(self, cnf, key: str = None):
        cur_config, config_file = self.get_current_config()
        if key:
            cur_config[key] = cnf
        else:
            cur_config = cnf
        with open(config_file, 'w', encoding='utf8') as wf:
            json.dump(cur_config, wf, indent=2, ensure_ascii=False)

    def get_access_token(self):
        # 内存获取
        if self.access_token:
            return self.access_token
        # 文件获取
        access_token, config_file = self.get_current_config(self.__ACCESS_TOKEN_KEY)
        if access_token:
            self.access_token = access_token
            return self.access_token
        return self.request_access_token()  # 网络获取

    # 获取当前主力代码
    def get_main_codes(self):
        # 内存获取
        if self.main_codes:
            return self.main_codes
        # 文件获取
        self.main_codes, _ = self.get_current_config(key=self.__MAIN_CODE_KEY)
        return self.main_codes

    # 从品种代码获取主力合约代码
    def get_main_code_with_variety(self, variety):
        main_codes = self.get_main_codes()
        for c in main_codes:
            if c['scode'] == variety:
                return c
        return None

    # 获取access_token
    def request_access_token(self):
        # 相当于登录接口，access_token7天有效，到期前3小时获取会刷新
        access_token_url = 'https://ft.10jqka.com.cn/api/v1/get_access_token'
        try:
            access_response = requests.get(url=access_token_url, headers={
                'ContentType': 'application/json',
                'refresh_token': self.__REFRESH_TOKEN
            })
            self.access_token = json.loads(access_response.content)['data']['access_token']
        except Exception as e:
            Logger().fetal(fetal='同花顺获取AccessToken失败:{}'.format(e), prefix=self.log_file_prefix)
            raise ValueError('同花顺AccessToken错误!')
        else:
            self.set_current_config(self.access_token, key=self.__ACCESS_TOKEN_KEY)  # 设置到文件
            return self.access_token
        
    def get_cache_file_quotation(self, file_prefix):
        quote_files = list(self.real_time_quotation_cache_folder.glob('{}*.json'.format(file_prefix)))
        quote_files.sort(reverse=True)
        if len(quote_files) > 0:
            last_file_time = quote_files[0].name.replace(f'{file_prefix}', '').split('.', 1)[0]
            seconds_diff = datetime_utils.calculate_seconds_diff(last_file_time, link='_')
            # print('utils/ths_api.py', 'seconds_diff:', seconds_diff, 's、out_of_exchange:', datetime_utils.is_out_of_exchange(dt=last_file_time, link='_'))
            if seconds_diff < 60 or datetime_utils.is_out_of_exchange(dt=last_file_time, link='_'):
                with open(quote_files[0], 'r', encoding='utf8') as qf:
                    quotations = json.load(qf)
                return quotations
        return []

    def set_cache_file_quotation(self, quote, file_prefix):
        quota_filename = '{}{}.json'.format(file_prefix, datetime_utils.now_yyyy_mm_dd_hhmmss(link='_'))
        with open(self.real_time_quotation_cache_folder.joinpath(quota_filename), 'w', encoding='utf8') as wf:
            json.dump(quote, wf, indent=2, ensure_ascii=False)

    def get_main_market_hot(self, codes, indicators, file_prefix):
        quotation = self.get_cache_file_quotation(file_prefix)
        if quotation:
            return quotation
        main_quote = self.get_real_quotation(codes, indicators, file_prefix)
        code_names = {c['code']: c['name'] for c in self.main_codes}
        for row in main_quote:
            row['name'] = code_names.get(row['code'], '-')
        self.set_cache_file_quotation(quote=main_quote, file_prefix=file_prefix)
        return main_quote

    def get_index_real_quote(self, codes, indicators, file_prefix):
        quotation = self.get_cache_file_quotation(file_prefix)
        if quotation:
            return quotation
        index_quote = self.get_real_quotation(codes, indicators, file_prefix)
        self.set_cache_file_quotation(quote=index_quote, file_prefix=file_prefix)
        return index_quote
    
    def get_real_quotation(self, codes, indicators, file_prefix):
        # 请求数据
        url = "https://quantapi.51ifind.com/api/v1/real_time_quotation"
        headers = {
            'ContentType': 'application/json',
            'access_token': self.get_access_token()
        }
        form = {
            'codes': ','.join(codes),
            'indicators': ','.join(indicators)
        }
        try:
            r = requests.post(url, json=form, headers=headers)
            res_data = r.json()
            if str(res_data['errorcode']) == str(self.TOKEN_EXPIRED):
                self.access_token = self.request_access_token()
                return self.get_real_quotation(codes, indicators, file_prefix)
            if res_data['errorcode'] != 0:
                raise ValueError(f'{res_data["errorcode"]}:{res_data["errmsg"]}')
        except Exception as e:
            Logger().fetal(str(e), prefix=self.log_file_prefix)
            return []
        else:
            quotations = self.to_dict(res_data['tables'])
            Logger().infor('获取实时行情成功!', prefix=self.log_file_prefix)
            return quotations

    def get_candle_daily(self, codes, indicators, start_date: str, end_date: str):
        url = "https://quantapi.51ifind.com/api/v1/cmd_history_quotation"
        headers = {
            'ContentType': 'application/json',
            'access_token': self.get_access_token()
        }
        form = {
            'codes': ','.join(codes),
            'indicators': ','.join(indicators),
            'startdate': start_date,
            'enddate': end_date
        }
        try:
            r = requests.post(url, json=form, headers=headers)
            res_data = r.json()
            if str(res_data['errorcode']) == str(self.TOKEN_EXPIRED):
                self.access_token = self.request_access_token()
                return self.get_candle_daily(codes, indicators, start_date, end_date)
            if res_data['errorcode'] != 0:
                raise ValueError(f'{res_data["errorcode"]}:{res_data["errmsg"]}')
        except Exception as e:
            Logger().fetal(str(e), prefix=self.log_file_prefix)
            return []
        else:
            quotations = self.to_dict_multi(res_data['tables'])
            Logger().infor('获取历史日行情成功!', prefix=self.log_file_prefix)
            return quotations

    def get_future_variety_real_quotation(self, variety):
        main_code = self.get_main_code_with_variety(variety)
        if not main_code:
            return []
        file_prefix = f'future_{variety}_'
        quotation = self.get_cache_file_quotation(file_prefix)
        if quotation:
            return quotation[0]
        indicators = ['tradeDate', 'tradeTime', 'latest', 'change', 'changeRatio']
        variety_quote = self.get_real_quotation(codes=[main_code['code']], indicators=indicators, file_prefix=file_prefix)
        self.set_cache_file_quotation(quote=variety_quote, file_prefix=file_prefix)
        return variety_quote[0] if variety_quote else []

    def to_dict(self, tables):
        res_data = []
        for row in tables:
            _obj = {
                'code': row['thscode'],
            }
            _obj.update({k: v[0] for k, v in row['table'].items()})
            res_data.append(_obj)
        return res_data

    def to_dict_multi(self, tables):
        quotation = {}
        for t_data in tables:
            code_data = []
            for i, t in enumerate(t_data['time']):
                day_obj = {
                    'code': t_data['thscode'],
                    'time': t
                }
                for key, v_list in t_data['table'].items():  # key: [value_list]
                    day_obj[key] = v_list[i]
                code_data.append(day_obj)
            quotation[t_data['thscode']] = code_data
        return quotation


